package com.cml.dm.admin.service.impl;

import cn.hutool.core.io.IoUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cml.dm.admin.entity.DmExeLogger;
import com.cml.dm.admin.entity.DmMetadata;
import com.cml.dm.admin.event.BackupEvent;
import com.cml.dm.admin.event.RecoveryEvent;
import com.cml.dm.admin.mapper.DmExeLoggerMapper;
import com.cml.dm.admin.mapper.DmMetadataMapper;
import com.cml.dm.admin.model.DownloadModel;
import com.cml.dm.admin.request.BackupListParams;
import com.cml.dm.admin.service.DmEngineService;
import com.cml.dm.engine.context.DmContext;
import com.cml.dm.engine.exception.DMException;
import com.cml.dm.engine.model.Task;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.util.*;

@Service
public class DmEngineServiceImpl implements DmEngineService {

    private static final Logger logger = LoggerFactory.getLogger(DmEngineServiceImpl.class);

    @Autowired
    private DmExeLoggerMapper dmExeLoggerMapper;
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;
    @Autowired
    private DmMetadataMapper dmMetadataMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void backup(String taskID, String orgId, String shardingColumn) {
        Task task =DmContext.getTask(taskID);
        if(task != null){
            DmExeLogger dmExeLogger=new DmExeLogger();
            dmExeLogger.setOrgId(orgId);
            dmExeLogger.setTaskID(taskID);
            dmExeLogger.setShardingColumn(shardingColumn);
            dmExeLogger.setExeTime(new Date());
            dmExeLogger.setExeType(dmExeLogger.backup());
            dmExeLogger.setExeStatus(0);
            dmExeLogger.setVersion(Double.parseDouble(task.getVersion()));
            dmExeLoggerMapper.insert(dmExeLogger);
            applicationEventPublisher.publishEvent(new BackupEvent(dmExeLogger));
        }else{
            throw new RuntimeException("备份任务不存在，请正确配置！");
        }
    }

    @Override
    public List<DmExeLogger> getTaskList(String taskID, String orgId) {
        QueryWrapper<DmExeLogger> queryWrapper = new QueryWrapper<DmExeLogger>();
        queryWrapper.lambda().eq(DmExeLogger::getTaskID, taskID);
        queryWrapper.lambda().eq(DmExeLogger::getOrgId, orgId);
        queryWrapper.lambda().eq(DmExeLogger::getExeType, 1);
        List dmMetadataList = dmExeLoggerMapper.selectList(queryWrapper);
        return dmMetadataList;
    }

    @Override
    public List<DmMetadata> queryDmMetadata(DmMetadata dmMetadata) {
        //todo 根据参数查询所有dmMetadata
        QueryWrapper<DmMetadata> wrapper = Wrappers.query(dmMetadata);
        List<DmMetadata> metadata = dmMetadataMapper.selectList(wrapper);
        return metadata;
    }

    @Override
    public void recover(Long id) {
        DmExeLogger exeLogger = dmExeLoggerMapper.selectById(id);
        exeLogger.setExeType(exeLogger.recovery());
        Task task =DmContext.getTask(exeLogger.getTaskID());
        if(task != null){
            DmExeLogger dmExeLogger=new DmExeLogger();
            dmExeLogger.setOrgId(exeLogger.getOrgId());
            dmExeLogger.setTaskID(exeLogger.getTaskID());
            dmExeLogger.setExeTime(new Date());
            dmExeLogger.setExeType(dmExeLogger.recovery());
            dmExeLogger.setExeStatus(0);
            dmExeLogger.setVersion(Double.parseDouble(task.getVersion()));
            dmExeLoggerMapper.insert(dmExeLogger);
            applicationEventPublisher.publishEvent(new RecoveryEvent(exeLogger,exeLogger.getBackupPath()));
        }else{
            throw new RuntimeException("备份任务不存在，请正确配置！");
        }
    }

    @Override
    public void saveBatch(List<DmMetadata> metadatas) {
        dmMetadataMapper.insertBatch(metadatas);
    }

    @Override
    public List<DmExeLogger> backupList(BackupListParams params) {
        params = Optional.ofNullable(params).orElse(new BackupListParams());
        LambdaQueryWrapper<DmExeLogger> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(DmExeLogger::getExeType,1);
        wrapper = ObjectUtils.isEmpty(params.getOrgId()) ? wrapper : wrapper.eq(DmExeLogger::getOrgId,params.getOrgId());
        wrapper = ObjectUtils.isEmpty(params.getTaskID()) ? wrapper : wrapper.eq(DmExeLogger::getTaskID,params.getTaskID());
        wrapper = ObjectUtils.isEmpty(params.getStartDate()) ? wrapper : wrapper.ge(DmExeLogger::getExeTime,params.getStartDate());
        wrapper = ObjectUtils.isEmpty(params.getEndDate()) ? wrapper : wrapper.le(DmExeLogger::getExeTime,params.getEndDate());
        List<DmExeLogger> list = dmExeLoggerMapper.selectList(wrapper);
        return list;
    }

    @Override
    public DownloadModel download(Long id) {
        DownloadModel model = new DownloadModel();
        DmExeLogger exeLogger = dmExeLoggerMapper.selectById(id);
        if(null == exeLogger){
            throw new DMException("无附件内容");
        }
        String path = exeLogger.getBackupPath();
        File file = new File(path);
        if(!file.exists()){
            throw new DMException("无附件内容");
        }
        FileInputStream fis = null;
        ByteArrayOutputStream baos = null;
        byte[] bytes = new byte[16];
        byte[] data;
        try {
            fis = new FileInputStream(file);
            baos = new ByteArrayOutputStream();
            int len = 0;
            while ((len = fis.read(bytes)) != -1){
                baos.write(bytes, 0, len);
            }
            baos.flush();
            data = baos.toByteArray();
        }catch (Exception e){
            logger.error("读取附件失败",e);
            throw new DMException("读取附件失败");
        }finally {
            IoUtil.close(fis);
            IoUtil.close(baos);
        }
        model.setData(data);
        model.setFileName(file.getName());
        return model;
    }
}
